ci: add shfmt and shellcheck for shell scripts - #691
Merged
Conversation
kolyshkin
marked this pull request as ready for review
August 26, 2026 02:00
jnovy
reviewed
Aug 26, 2026
The script sets up a Cirrus-CI VM in GCP, and conmon stopped using Cirrus in e0c56ec ("ci: replace Cirrus CI with GitHub Actions"). Its --setup path sources ./contrib/cirrus/lib.sh and runs ./contrib/cirrus/setup_environment.sh, both of which were deleted in 42cecdf ("Cirrus: Remove disused scripts"), so that path cannot work at all any more. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add an .editorconfig based on the one podman uses, reformat every shell script in the tree with shfmt, and add a CI job that keeps it that way. The .editorconfig is podman's file minus space_redirects, so redirections keep the shape they already have here. The [[shell]] section is what lets shfmt pick up hack/github-actions-setup, which is a shell script with a shebang and no extension; that section is an shfmt extension rather than something editorconfig itself understands. See podman-container-tools/podman#28785. shfmt itself runs the way runc runs it: a make target using a pinned container image, so that a local run and a CI run cannot disagree about formatting, plus a localshfmt target for whoever already has shfmt installed. The CI job is then just "make shfmt". shfmt finds the files to format on its own, .bats files included, so no file list has to be maintained anywhere. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by shellcheck (SC2086). None of these were broken in practice, since the paths involved never contain whitespace or glob characters, but quoting them is what keeps that true. get_conmon_journal_output() is the one place where the word splitting was load-bearing: its level filter is either empty or "-p <level>", which has to reach journalctl as two separate arguments. Quoting it as it stood would have passed it as one, so it becomes an array instead. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by shellcheck (SC2155). "local x=$(cmd)" makes the exit status of local, which is always zero, mask the exit status of cmd. None of these four checked that status, so the bug is latent rather than real, but the next person to add "set -e" or a status check would not get what they expect. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by shellcheck (SC2140, SC2183). Both places build a 65535 character line the same way, and both were sloppy about it: The %*s conversion takes a width and a string, so "printf '%*s' 65535" was relying on printf treating the missing second argument as empty. Pass the empty string explicitly. In 02-ctr-logs.bats the whole command was also a double quoted string containing a bare "65535", which merely concatenated into the intended text rather than quoting anything. Drop the inner quotes. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by shellcheck (SC2218). The function sat at the bottom of the file while seven tests called it. bats sources the whole file before running any test, so this worked, but shellcheck reads the file as an ordinary script and cannot know that. Moving the definition next to the other helpers is also just easier to read. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Five places where shellcheck is reading the code correctly but cannot know the context, so they get a directive with the reason: - two single quoted strings (SC2016) hold script text meant to be expanded somewhere else, once in the container and once in a child shell that inherits an exported TEST_TMPDIR; - two files source /etc/os-release (SC1091), which shellcheck cannot follow because it is not part of the tree; - in test_helper.bash, status and output (SC2154) come from bats' run, and variables such as VALID_PATH (SC2034) are only referenced by the .bats files that load the helper. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Run it the same way as shfmt: a make target using a pinned container image, a localshellcheck target for whoever has shellcheck installed, and a CI job that is just "make shellcheck". Pinning matters more here than it does for shfmt, since what shellcheck reports moves between releases and ubuntu-latest is several versions behind. The tree is clean as of the preceding commits. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collaborator
Author
|
rebased (conflicts in Makefile resolved) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds shfmt and shellcheck to CI, plus the fixes needed to make the tree clean under both.
Both run the way runc runs shfmt: a make target using a pinned container image, so a local run and a CI run cannot disagree, plus
localshfmt/localshellchecktargets for whoever already has the tools installed. The CI jobs are then justmake shfmtandmake shellcheck. Pinning matters more for shellcheck than for shfmt, since what it reports moves between releases and ubuntu-latest is several versions behind.hack/get_ci_vm.sh
Removed first, because it is dead. It sets up a Cirrus-CI VM in GCP, and conmon stopped using Cirrus in e0c56ec; its
--setuppath sources./contrib/cirrus/lib.shand runs./contrib/cirrus/setup_environment.sh, both deleted in 42cecdf. shellcheck is what pointed this out, via the SC1091 on thatsourceline.shfmt
The
.editorconfigis podman's file minusspace_redirects, so redirections keep the shape they already have here. The[[shell]]section is what lets shfmt pick uphack/github-actions-setup, a shell script with a shebang and no extension; that section is an shfmt extension rather than something editorconfig itself understands. See podman-container-tools/podman#28785.shfmt finds the files to format on its own,
.batsfiles included, so no file list has to be maintained.shellcheck
One commit per theme: quoting (SC2086), declare-and-assign (SC2155), the printf padding trick (SC2140, SC2183), moving a helper above its callers (SC2218), and directives for the places shellcheck cannot know the context (SC2016, SC1091, SC2034, SC2154). No check is disabled globally.
None of the findings was an actual bug. The one that needed care is
get_conmon_journal_output(), where the word splitting was load-bearing: its level filter is either empty or-p <level>, which has to reach journalctl as two arguments, so it becomes an array rather than a quoted string.Note
test/Makefilewas reformatted in passing before this branch and lost the tabs in itscleanrecipe, which brokemake -C test cleanwithmissing separator. That is reverted here. It may be worth an.editorconfigsection pinning Makefiles to tabs so it cannot happen again.